#include "../include/mysql_con_pool.h"
#include "../include/conf_reader.h"

mysql_con_pool* mysql_con_pool::instance = nullptr;

//外部接口
mysql_con_pool* mysql_con_pool::get_instance()
{
    //这里其实可以采用双检锁

    if (instance == nullptr) {
        instance = new mysql_con_pool();    //无参构造函数创建这个线程池
        static for_mysql_con_pool  p;   //就是为了正确释放instance的
    }
    return instance;
}


mysql_con_pool::mysql_con_pool(){
    create_pool();
}

MYSQL* mysql_con_pool::get_con()
{
    MYSQL* con = NULL;
    std::unique_lock<std::mutex> lock(this->mtu);   //已经自动上锁了
    if (!this->con_pool.empty()) {
        con = this->con_pool.front();
        this->con_pool.pop();
    }
    lock.unlock();  //这行其实多余的，因为lock超出作用域后析构里自动unlock了
    
    return con;
}

void mysql_con_pool::return_con(MYSQL*con)
{
    if (mysql_ping(con) != 0) {
        fprintf(stderr, "mysql_ping error:%s", mysql_error(con));
        return;
    }

    {
        std::lock_guard<std::mutex>lock(this->mtu);
        this->con_pool.push(con);
    }
    
}

void mysql_con_pool::get_mysql_conf()
{
    conf_reader* reader=conf_reader::get_conf_reader_instance();
    this->host=reader->get_value_from_key("host","192.168.174.139");
    this->user=reader->get_value_from_key("user","root");
    this->passwd=reader->get_value_from_key("passwd","Root1234!");
    this->db_name=reader->get_value_from_key("db_name","file_system");
    this->port=atoi(reader->get_value_from_key("port","3306"));
    this->char_set=reader->get_value_from_key("char_set","utf8");
    this->num_of_con=atoi(reader->get_value_from_key("num_of_con","20"));
}

void mysql_con_pool::create_pool()
{
    //先读取配置文件
    this->get_mysql_conf();
    for (int i = 0; i < this->num_of_con; ++i) {
        MYSQL * con= mysql_init(NULL);
        if (con == NULL) {
            perror("con 初始化失败：");
            continue;
        }
        if (mysql_options(con, MYSQL_SET_CHARSET_NAME, this->char_set.c_str()) != 0) {
            perror("mysql_options error:");
            mysql_close(con);
        }
        if (mysql_real_connect(con, this->host.c_str(), this->user.c_str(), this->passwd.c_str(), this->db_name.c_str(), this->port, NULL, 0) == NULL) {
            fprintf(stderr, "mysql_real_connect error:%s", mysql_error(con));
            mysql_close(con);
        }
        this->con_pool.push(con);
        this->for_free.push_back(con);
        printf("成功建立第%d个连接\n", i);
    }

}

MYSQL_RES* mysql_con_pool::get_query_result_by_store(MYSQL*con, const char*sql)
{
    if (mysql_ping(con) != 0) {
        fprintf(stderr, "查询数据时，mysql_ping failed: %s\n", mysql_error(con));
        return NULL;
    }
    if (mysql_query(con, sql) != 0) {
        fprintf(stderr, "查询数据时，mysql_query failed: %s \n", mysql_error(con));
        return NULL;
    }
    MYSQL_RES * res= mysql_store_result(con);
    if (res == NULL) {  //执行的sql语句不会有数据返回 （如insert语句）或者拿取出错
        fprintf(stderr, "查询数据时，mysql_store_result failed: %s \n", mysql_error(con));
        return NULL;
    }
    if (mysql_num_rows(res) == 0) {
        //执行了sql语句，但是结果集为空  比如执行了select ,但是没有符合的数据
        mysql_free_result(res);
        return NULL;
    }

    return res;
}

MYSQL_RES* mysql_con_pool::get_query_result_by_use(MYSQL*con, const char*sql)
{
    if (mysql_ping(con) != 0) {
        fprintf(stderr, "查询数据时，mysql_ping failed: %s\n", mysql_error(con));
        return NULL;
    }
    if (mysql_query(con, sql) != 0) {
        fprintf(stderr, "查询数据时，mysql_query failed: %s \n", mysql_error(con));
        return NULL;
    }
    MYSQL_RES* res = mysql_use_result(con);
    if (res == NULL) {  //或者拿取出错
        fprintf(stderr, "查询数据时，mysql_store_result failed: %s \n", mysql_error(con));
        return NULL;
    }

    return res;
}



mysql_con_pool::~mysql_con_pool()
{
    int i = 0;
    //为了资源的释放-----其实好像没有什么必要，因为要析构就表明程序要退出，要退出，操作系统会自动释放进程资源
    for (; i < this->for_free.size(); ++i) {
        mysql_close(for_free[i]);   //强制关闭
    }
    for_free.clear();
    
    while (!con_pool.empty()) {
        con_pool.pop();
    }
    std::cout << "析构函数成功释放" << i << "个" << std::endl;
    
    
}
